package com.atguigu.spzx.manager.interceptor;

import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.common.util.LoginAuthUtil;
import com.atguigu.spzx.model.entity.system.SysUser;
import com.atguigu.spzx.model.vo.common.Result;
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class LoginAuthInterceptor implements HandlerInterceptor {
    //需要从容器中装配对象，当前类的对象必须注入到容器中
    @Resource
    StringRedisTemplate stringRedisTemplate;
    // 多线程并发时，获取到的CACHE是同一个Map：HashMap线程不安全(方法未加锁)
    // 可以使用线程安全的Map：Vector(性能较差)  ConcurrentHashMap
    //使用线程安全的Map存储： 所有线程使用同一个Map，使用线程对象作为键绑定数据
//    public static final Map<Thread, SysUser> CACHE = new ConcurrentHashMap<>();
    //使用线程对象自带的成员变量Map： 每个线程使用自己的Map，每个线程绑定数据时 都使用了THREADLOCAL对象作为键
    //   线程安全的原因：线程的栈数据是私有的   性能高 没有多线程的竞争关系
//    public static final ThreadLocal<SysUser> THREAD_LOCAL  = new ThreadLocal<>();
    //放行前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1、预检请求直接放行
        if("OPTIONS".equals(request.getMethod().toUpperCase())){
            return true;
        }
        //2、获取请求头中的token
        String token = request.getHeader("token");
        if(!StringUtils.hasLength(token)){
            //拒绝请求：未登录
            genResponse(response,ResultCodeEnum.LOGIN_ACCESS_ERROR);
            return false;
        }
        String sysUserJson = stringRedisTemplate.opsForValue().get("user:login:" + token);
        if(!StringUtils.hasLength(sysUserJson)){
            //拒绝请求：登录过期
            //1、响应状态码
            genResponse(response,ResultCodeEnum.LOGIN_EXPIRE_ERROR);
            return false;
        }
        //将校验登录状态查询到的sysUser缓存 交给后续的业务使用，后续业务无需再查询数据了..
//        CACHE.put(Thread.currentThread() , JSON.parseObject(sysUserJson, SysUser.class));

        //THREAD_LOCAL存数据时：先获取线程对象的Map 再使用THREAD_LOCAL对象作为键，传入的数据作为值 存到当前线程对象的Map中
        LoginAuthUtil.set(JSON.parseObject(sysUserJson, SysUser.class));
        //放行请求
        return true;
//        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
    //构建响应报文： ctrl+alt+m   ctrl+alt+v
    private static void genResponse(HttpServletResponse response , ResultCodeEnum codeEnum) throws IOException {
        response.setStatus(HttpServletResponse.SC_OK);//响应报文的状态码
        //2、响应头：配置响应体内容的格式类型（前端根据内容解析响应体的数据）
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        //响应体: 设置一个状态码为208 需要登录的状态码
        Result<Object> result = Result.build(codeEnum, null);
        String jsonString = JSON.toJSONString(result);
        response.getWriter().print(jsonString);//将json写入到响应体中
    }

    //响应前执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        //移除ThreadLocal中的缓存：不移除会导致内存泄漏(OOM)
        LoginAuthUtil.remove();

    }
}
